Skip to content

fix: serve the pdf.js data files from a controller instead of as plain files - #1547

Open
ByteSide wants to merge 1 commit into
nextcloud:masterfrom
ByteSide:fix/serve-pdfjs-data-files-from-controller
Open

fix: serve the pdf.js data files from a controller instead of as plain files#1547
ByteSide wants to merge 1 commit into
nextcloud:masterfrom
ByteSide:fix/serve-pdfjs-data-files-from-controller

Conversation

@ByteSide

@ByteSide ByteSide commented Sep 4, 2026

Copy link
Copy Markdown

Summary

pdf.js loads its localization (locale.json and <lang>/viewer.ftl), the CMaps for CJK documents (*.bcmap) and the standard fonts (*.pfb, *.ttf) at runtime, relative to a base URL. They were linked as plain files below apps/files_pdfviewer/js/pdfjs/web/, which only works when the web server serves those file extensions directly.

With pretty URLs enabled on Apache, everything not on the static extension list of the generated front controller rule (css|js|mjs|svg|…) is handed to index.php, which answers 404 for all four. The viewer then shows the alt text dialog instead of the document (nextcloud/server#63512, reported on 34.0.3 and 33.0.8), and CJK documents render as blank pages (#873, open since 2023). The official Docker image enables pretty URLs, so this affects every Apache install based on it.

This PR serves the files from an AssetController through /apps/files_pdfviewer/assets/{version}/{path} instead:

  • only the three directories locale/, cmaps/ and standard_fonts/ below js/pdfjs/web/ are reachable, and inside them only the extensions pdf.js needs (json, ftl, bcmap, pfb, ttf); every segment is checked and the resolved path is verified to stay inside the directory
  • the {version} segment is the hash the viewer already appends to its other assets, so the responses are cached as immutable for the same lifetime .htaccess gives the other viewer assets
  • templates/viewer.php points cMapUrl and the l10n <link> at the route, and now also sets standardFontDataUrl, which pdf.js otherwise derives relative to viewer.mjs (the same 404 for the .pfb fonts)

This is the approach suggested in nextcloud/server#63947: keep the web server configuration as it is and let the app serve what it needs within the framework, rather than growing the static extension allow list with every pdf.js release (.properties.json/.ftl.bcmap/.pfb).

Testing

Against nextcloud/server master in the nextcloud:34-fpm image (PHP 8.5), with the app mounted into apps/:

  • composer run test:unit: 20 tests, 83 assertions, green (17 of them new)
  • psalm: no errors; php-cs-fixer: nothing to fix
  • through php -S against index.php, i.e. the worst case where nothing is served statically: locale/locale.json, locale/pt-BR/viewer.ftl, cmaps/Adobe-Japan1-UCS2.bcmap, standard_fonts/FoxitSerif.pfb and standard_fonts/LiberationSans-Regular.ttf all return 200 with the expected content type, Content-Length and Cache-Control: private, max-age=15778463, immutable; the CMap is byte-identical to the file on disk. locale/../../build/pdf.mjs, ../viewer.mjs, locale/README.md and a missing file return 404.
  • the rendered viewer page carries data-cmapurl, data-standardfontdataurl and the l10n link on the new route, with the version hash in the path

The compiled bundles are included (node 24 / npm 11 as in engines); the pdf.js dist re-fetched by npm run build came back identical, so only the two app chunks changed.

Checklist

  • Code is properly formatted
  • Sign-off message is added to all commits
  • Tests (unit) are included
  • Screenshots before/after for front-end changes — no visible change
  • Documentation has been updated or is not required
  • Backports requested where applicable — stable34 and stable33 carry the same template and pdf.js 4.10.38, please backport if you agree

…n files

pdf.js loads its localization (locale.json and <lang>/viewer.ftl), the
CMaps for CJK documents (*.bcmap) and the standard fonts (*.pfb, *.ttf)
at runtime, relative to a base URL. They were linked as plain files below
apps/files_pdfviewer/js/pdfjs/web/, which only works when the web server
serves those file extensions directly.

With pretty URLs enabled on Apache, everything not on the static extension
list of the front controller rule is handed to index.php, which answers
404 for all four: the viewer then shows the alt text dialog instead of the
document, and CJK documents render blank. The official Docker image
enables pretty URLs, so this affects every Apache install based on it.

The files are now served by AssetController through a route below
/apps/files_pdfviewer/assets/, restricted to the three directories and
the file extensions pdf.js needs from them. The version hash the viewer
already appends to its other assets is part of the URL, so the responses
can be cached as immutable for the same lifetime the web server gives the
other assets. The template points cMapUrl, the l10n link and the newly
set standardFontDataUrl at that route.

Resolves: nextcloud/server#63512
Resolves: nextcloud#873
Signed-off-by: Stefan - ByteSide.io <sr@byteside.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: PDF viewer broken due to .json and .ftl rewrite rules Fail to render / load Japanese fonts

1 participant